/*
   Copyright 2009 Bloo. All Rights Reserved.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
package org.youtensils.www.client.widgets;

import java.util.ArrayList;
import java.util.LinkedList;

import org.youtensils.www.client.events.DownloadListener;

import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.NamedFrame;

/**
 * A widget providing video download and audio encoding through the use of a hidden Java applet.
 * @author Bobby Soares
 */
public class DownloadEngine extends Composite {
	public static DownloadEngine activeEngine = null;
	private ArrayList<DownloadListener> listeners;
	private LinkedList<String[]> calls;
	private NamedFrame contentPane;
	private boolean locked = false, running = false, downloading = false;
	/**
	 * The default constructor.
	 */
	public DownloadEngine(){
		activeEngine = this;
		this.listeners = new ArrayList<DownloadListener>();
		this.calls = new LinkedList<String[]>();
		this.setupJavascriptInterface(this);
		this.contentPane = new NamedFrame("Engine");
		this.contentPane.setPixelSize(0, 0);
		this.contentPane.setUrl("Empty.html");
		this.contentPane.getElement().setPropertyString("frameborder", "0");
    	initWidget(this.contentPane);
    	setStyleName("gwt-DownloadEngine");
	}
	/** Retrieves a value indicating whether the Java applet has started successfully.
	 * @return whether the applet has started
	 */
	public boolean isStarted(){
		return this.running;
	}
	/** Retrieves a value indicating whether the Java applet is running inside a security sandbox.
	 * @return whether the applet is running inside a security sandbox
	 */
	public boolean isLocked(){
		return this.locked;
	}
	/**
	 * Starts the Java applet component of this widget.
	 */
	public void start(){
		if(!this.running){
			this.running = true;
			this.contentPane.setUrl("Applet.html");
		}
	}
	/**
	 * Stops the Java applet component of this widget.
	 */
	public void stop(){
		if(this.running){
			this.running = false;
			this.contentPane.setUrl("Empty.html");
		}
	}
	/**
	 * Adds a download event listener.
	 * @param the download event listener to register
	 */
	public void addListener(DownloadListener listener){
		this.listeners.add(listener);
	}
	/**
	 * Cancels a given download.
	 * @param id the id of the download which should be canceled
	 */
	public void cancelDownload(String id){
		if(this.locked) return;
		this.calls.add(new String[]{ "cancelDownload", id });
	}
	/**
	 * Starts a download.
	 * @param id the download id
	 * @param format the download format
	 */
	public void startDownload(String id, String format){
		if(this.locked){
			Window.alert("The Java applet was not allowed to execute. You will need to " +
					"refresh your browser and allow the applet to run when prompted.");
			return;
		}
		this.calls.add(new String[]{ "startDownload", id, format });
	}
	/**
	 * Retrieves a value indicating whether the Java applet is actively downloading an item.
	 * @return whether a download is in progress
	 */
	public boolean hasActiveItem(){
		if(this.locked) return false;
		return this.downloading;
	}
	/**
	 * Signals that the Java sandbox has been detected.
	 */
	public void onSandboxDetected(){
		this.locked = true;
		this.downloading = false;
		this.stop();
		//cancel all
		try{
			for(int i=0; i<this.calls.size(); i++){
				String[] call = this.calls.get(i);
				if(call.length > 1){
					onCancelItem(call[1]);
				}
			}
		}catch(Exception x){}
		for(int i=0; i<this.listeners.size(); i++){
			this.listeners.get(i).onSandboxDetected(this);
		}
	}
	/**
	 * Signals that the progress for a given download has changed.
	 * @param id the download id
	 * @param totalSteps the total number of steps required to successful download completion
	 * @param currentStep the current step
	 * @param totalBytes the total byte size of the item being downloaded
	 * @param loadedBytes the amount of bytes that have loaded for the item being downloaded
	 * @param label a label describing the current step
	 */
	public void onSetItemProgress(String id, int totalSteps, int currentStep, int totalBytes, int loadedBytes, String label){
		this.downloading = true;
		for(int i=0; i<this.listeners.size(); i++){
			this.listeners.get(i).onDownloadProgress(this, id, totalSteps, currentStep, totalBytes, loadedBytes, label);
		}
	}
	/**
	 * Signals that a download item has been opened.
	 * @param id the download id
	 */
	public void onOpenItem(String id){
		try{
			this.downloading = true;
			for(int i=0; i<this.listeners.size(); i++){
				this.listeners.get(i).onBeginDownload(this, id);
			}
		}catch(Exception x){}
	}
	/**
	 * Signals that a download item has been closed.
	 * @param id the download id
	 */
	public void onCloseItem(String id){
		try{
			this.downloading = false;
			for(int i=0; i<this.listeners.size(); i++){
				this.listeners.get(i).onCompleteDownload(this, id);
			}
		}catch(Exception x){}
	}
	/**
	 * Signals that a download item has been canceled
	 * @param id the download id
	 */
	public void onCancelItem(String id){
		try{
			this.downloading = false;
			for(int i=0; i<this.listeners.size(); i++){
				this.listeners.get(i).onCancelDownload(this, id);
			}
		}catch(Exception x){}
	}
	/**
	 * Signals that a download item errored.
	 * @param id the download id
	 * @param error the error description
	 */
	public void onErrorItem(String id, String error){
		try{
			this.downloading = false;
			for(int i=0; i<this.listeners.size(); i++){
				this.listeners.get(i).onErrorDownload(this, id, error);
			}
		}catch(Exception x){}
	}
	/**
	 * Retrieves the next operation that should be passed to the Java applet.
	 * The Java applet will call this method to determine what action it should take next.
	 * @return a string array specifying method and parameter information
	 */
	public String[] getNextCall(){
		String[] next = this.calls.poll();
		return next;
	}
	/**
	 * Exposes this widget's API to the HTML page for direct use by the Java applet component.
	 * @param eng the DownloadEngine widget which will handle the JavaScript side of the Java-JavaScript communication.
	 */
	public native void setupJavascriptInterface(DownloadEngine eng) /*-{
		$wnd.downloadEngine = { };
		$wnd.downloadEngine.onSetItemProgress = function(s1, i1, i2, i3, i4, s2){
			return @org.youtensils.www.client.widgets.DownloadEngine::activeEngine.@org.youtensils.www.client.widgets.DownloadEngine::onSetItemProgress(Ljava/lang/String;IIIILjava/lang/String;)(s1, i1, i2, i3, i4, s2);
		}
		$wnd.downloadEngine.onOpenItem = function(s1){
			return @org.youtensils.www.client.widgets.DownloadEngine::activeEngine.@org.youtensils.www.client.widgets.DownloadEngine::onOpenItem(Ljava/lang/String;)(s1);
		}
		$wnd.downloadEngine.onCloseItem = function(s1){
			return @org.youtensils.www.client.widgets.DownloadEngine::activeEngine.@org.youtensils.www.client.widgets.DownloadEngine::onCloseItem(Ljava/lang/String;)(s1);
		}
		$wnd.downloadEngine.onCancelItem = function(s1){
			return @org.youtensils.www.client.widgets.DownloadEngine::activeEngine.@org.youtensils.www.client.widgets.DownloadEngine::onCancelItem(Ljava/lang/String;)(s1);
		}
		$wnd.downloadEngine.onErrorItem = function(s1, s2){
			return @org.youtensils.www.client.widgets.DownloadEngine::activeEngine.@org.youtensils.www.client.widgets.DownloadEngine::onErrorItem(Ljava/lang/String;Ljava/lang/String;)(s1, s2 || null);
		}
		$wnd.downloadEngine.onSandboxDetected = function(){
			return @org.youtensils.www.client.widgets.DownloadEngine::activeEngine.@org.youtensils.www.client.widgets.DownloadEngine::onSandboxDetected()();
		}
		$wnd.downloadEngine.getNextCall = function(){
			var nc = @org.youtensils.www.client.widgets.DownloadEngine::activeEngine.@org.youtensils.www.client.widgets.DownloadEngine::getNextCall()();
			if (nc) return @org.youtensils.www.client.impl.ArrayHelper::fromArray([Ljava/lang/String;)(nc);
			else return null;
		}
		$wnd.document.body.onbeforeunload = function(){
			if(@org.youtensils.www.client.widgets.DownloadEngine::activeEngine.@org.youtensils.www.client.widgets.DownloadEngine::hasActiveItem()()){
				return '';
			}else{
				@org.youtensils.www.client.widgets.DownloadEngine::activeEngine.@org.youtensils.www.client.widgets.DownloadEngine::stop()();
			}
		}
	}-*/;
}
